package de.sendorian.app.forumArchive.finder;

import static de.sendorian.util.matcher.FileTypeMatchers.archive;
import static org.apache.commons.lang.StringEscapeUtils.unescapeHtml;
import static org.apache.commons.lang.StringUtils.trim;
import static org.apache.commons.lang.StringUtils.trimToNull;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.anything;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.startsWith;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.log4j.Logger;
import org.hamcrest.Matcher;
import org.htmlparser.Node;
import org.htmlparser.filters.TagNameFilter;
import org.htmlparser.nodes.TagNode;
import org.htmlparser.util.NodeList;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import de.sendorian.app.forumArchive.domain.Archive;
import de.sendorian.app.forumArchive.domain.Image;
import de.sendorian.app.forumArchive.domain.Link;
import de.sendorian.app.forumArchive.domain.Post;
import de.sendorian.app.forumArchive.domain.Topic;
import de.sendorian.app.forumArchive.domain.User;
import de.sendorian.app.forumArchive.repository.UserRepository;


@Component
public abstract class PostFinder extends FinderBase<Topic, Post> {

	private static final Logger LOG = Logger.getLogger(PostFinder.class);

	protected Matcher<Post> matcher = anything();

	protected UserRepository userRepository = new UserRepository();

	protected @Value("#{config.baseUrl}") String baseUrl;

	protected Matcher<String> externalLinkMatcher;

	protected Matcher<String> internalLinkMatcher;


	public PostFinder() {
	}


	public PostFinder(Matcher<Post> matcher) {
		this.matcher = matcher;
	}


	public List<Post> find(Topic topic) {
		List<Post> posts = find(topic, topic.getUrl());
		LOG.info("Found " + posts.size() + " posts in '" + topic.getName() + "'");
		return posts;
	}


	private List<Post> find(Topic topic, String url) {
		List<Post> posts = new ArrayList<Post>();
		NodeList tableRows = parse(url, new TagNameFilter("tr"), false);

		LOG.debug("Found posts in '" + topic.getName() + "':");
		for (Node node : tableRows.toNodeArray()) {
			TagNode row = (TagNode) node;
			String author = getAuthor(row);

			if (author != null) {
				Post post = new Post();
				Date postDate = getPostDate(row);
				post.setPostDate(postDate);
				String userName = trimToNull(unescapeHtml(author));
				if (userName != null) {
					User user = userRepository.findByName(userName);
					if (user == null) {
						user = new User(userName);
						userRepository.save(user);
					}
					post.setAuthor(user);
				}
				post.setMessage(trim(unescapeHtml(getMessage(row))));
				post.setImages(getImages(row));
				post.setArchives(getArchives(row));
				post.setLinks(getLinks(row));

				if (matcher.matches(post)) {
					posts.add(post);
					LOG.debug(post.toString());
				}
			}
		}

		String prevPage = getPreviousPage(tableRows);
		if (prevPage != null) {
			List<Post> prevPosts = find(topic, prevPage);
			prevPosts.addAll(posts);
			posts = prevPosts;
		}

		return posts;
	}


	public Matcher<String> getExternalLinkMatcher() {
		if (externalLinkMatcher == null) {
			externalLinkMatcher = allOf(is(not(archive())), not(startsWith(baseUrl)), not(containsString("mailto")));
		}
		return externalLinkMatcher;
	}


	public Matcher<String> getInternalLinkMatcher() {
		if (internalLinkMatcher == null) {
			internalLinkMatcher = allOf(is(not(archive())), startsWith(baseUrl), not(containsString("mailto")));
		}
		return internalLinkMatcher;
	}


	abstract protected String getAuthor(TagNode tr);


	abstract protected Date getPostDate(TagNode tr);


	abstract protected String getMessage(TagNode tr);


	protected abstract List<Archive> getArchives(TagNode row);


	protected abstract List<Image> getImages(TagNode row);


	protected abstract List<Link> getLinks(TagNode row);


	abstract protected String getPreviousPage(NodeList tableRows);
}
